﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using IPC.Communication.Framework.BasicFramework;

namespace IPC.Communication.Framework.Core.Net
{
    public class NetworkUdpBase : NetworkBase
    {
        protected bool LogMsgFormatBinary = true;

        private SimpleHybirdLock hybirdLock = null;

        private int connectErrorCount = 0;

        private string ipAddress = "127.0.0.1";
        private int _receiveCacheLength = 2048;

        public virtual string IpAddress
        {
            get
            {
                return ipAddress;
            }
            set
            {
                ipAddress = HslHelper.GetIpAddressFromInput(value);
            }
        }

        public virtual int Port
        {
            get;
            set;
        }

        public int ReceiveTimeout
        {
            get;
            set;
        }

        public string ConnectionId
        {
            get;
            set;
        }

        public int ReceiveCacheLength
        {
            get { return _receiveCacheLength; }
            set { _receiveCacheLength = value; }
        }


        public IPEndPoint LocalBinding
        {
            get;
            set;
        }

        public NetworkUdpBase()
        {
            hybirdLock = new SimpleHybirdLock();
            ReceiveTimeout = 5000;
            ConnectionId = SoftBasic.GetUniqueStringByGuidAndRandom();
        }

        protected virtual byte[] PackCommandWithHeader(byte[] command)
        {
            return command;
        }

        protected virtual OperateResult<byte[]> UnpackResponseContent(byte[] send, byte[] response)
        {
            return OperateResult.CreateSuccessResult(response);
        }

        public virtual OperateResult<byte[]> ReadFromCoreServer(byte[] send)
        {
            return ReadFromCoreServer(send, hasResponseData: true, usePackAndUnpack: true);
        }

        public virtual OperateResult<byte[]> ReadFromCoreServer(byte[] send, bool hasResponseData, bool usePackAndUnpack)
        {
            byte[] array = usePackAndUnpack ? PackCommandWithHeader(send) : send;
            if (base.LogNet != null)
            {
                base.LogNet.WriteDebug(ToString(), StringResources.Language.Send + " : " + (LogMsgFormatBinary ? SoftBasic.ByteToHexString(array) : Encoding.ASCII.GetString(array)));
            }
            hybirdLock.Enter();
            try
            {
                IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(IpAddress), Port);
                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                if (LocalBinding != null)
                {
                    socket.Bind(LocalBinding);
                }
                socket.SendTo(array, array.Length, SocketFlags.None, remoteEP);
                if (ReceiveTimeout < 0)
                {
                    hybirdLock.Leave();
                    return OperateResult.CreateSuccessResult(new byte[0]);
                }
                if (!hasResponseData)
                {
                    hybirdLock.Leave();
                    return OperateResult.CreateSuccessResult(new byte[0]);
                }
                socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, ReceiveTimeout);
                IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Any, 0);
                EndPoint remoteEP2 = iPEndPoint;
                byte[] array2 = new byte[ReceiveCacheLength];
                int length = socket.ReceiveFrom(array2, ref remoteEP2);
                byte[] array3 = array2.SelectBegin(length);
                hybirdLock.Leave();
                if (base.LogNet != null)
                {
                    base.LogNet.WriteDebug(ToString(), StringResources.Language.Receive + " : " + (LogMsgFormatBinary ? SoftBasic.ByteToHexString(array3) : Encoding.ASCII.GetString(array3)));
                }
                connectErrorCount = 0;
                return usePackAndUnpack ? UnpackResponseContent(array, array3) : OperateResult.CreateSuccessResult(array3);
            }
            catch (Exception ex)
            {
                hybirdLock.Leave();
                if (connectErrorCount < 100000000)
                {
                    connectErrorCount++;
                }
                return new OperateResult<byte[]>(-connectErrorCount, ex.Message);
            }
        }

        //[AsyncStateMachine(typeof(<ReadFromCoreServerAsync>d__29))]
        //[DebuggerStepThrough]
        //public Task<OperateResult<byte[]>> ReadFromCoreServerAsync(byte[] value)
        //{
        //    <ReadFromCoreServerAsync>d__29 stateMachine = new <ReadFromCoreServerAsync>d__29();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult<byte[]>>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.value = value;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        public IPStatus IpAddressPing()
        {
            Ping ping = new Ping();
            return ping.Send(IpAddress).Status;
        }

        public override string ToString()
        {
            return String.Format("NetworkUdpBase[{0}:{1}]", IpAddress, Port);
        }
    }
}
